package com.electromagnetic.industry.software.manage.config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.SystemClock;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.electromagnetic.industry.software.common.annotations.UserOperation;
import com.electromagnetic.industry.software.common.cons.UserConstants;
import com.electromagnetic.industry.software.common.enums.AdminTypeEnum;
import com.electromagnetic.industry.software.common.pojo.AccessSuccessInfo;
import com.electromagnetic.industry.software.common.pojo.UserLoginInfo;
import com.electromagnetic.industry.software.common.resp.ElectromagneticResult;
import com.electromagnetic.industry.software.common.util.EleIdCreator;
import com.electromagnetic.industry.software.common.util.TokenUtil;
import com.electromagnetic.industry.software.common.util.UserThreadLocal;
import com.electromagnetic.industry.software.manage.mapper.TokenMapper;
import com.electromagnetic.industry.software.manage.mapper.UserAccessLogMapper;
import com.electromagnetic.industry.software.manage.pojo.models.Token;
import com.electromagnetic.industry.software.manage.pojo.models.UserAccessLog;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    @Resource
    private TokenMapper tokenMapper;

    @Resource
    private UserAccessLogMapper userAccessLogMapper;

    @Resource
    private ElePropertyConfig elePropertyConfig;

    private static String getRealIp(HttpServletRequest request) {
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (StrUtil.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            // 回退到X-Real-IP或直接RemoteAddr
            ipAddress = request.getHeader("X-Real-IP");
        }
        if (StrUtil.isEmpty(ipAddress) || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        // 处理多级代理的情况（取第一个IP）
        if (ipAddress.contains(",")) {
            ipAddress = ipAddress.split(",")[0].trim();
        }
        return ipAddress;
    }

    private static String getSuffixFromUrl(String url) {
        if (url == null || url.isEmpty()) {
            return "";
        }

        // 先去除查询参数和锚点
        int queryIndex = url.indexOf('?');
        int fragmentIndex = url.indexOf('#');

        String path = url;
        if (queryIndex > 0) {
            path = url.substring(0, queryIndex);
        } else if (fragmentIndex > 0) {
            path = url.substring(0, fragmentIndex);
        }

        // 获取最后一个点之后的部分
        int lastDotIndex = path.lastIndexOf('.');
        if (lastDotIndex > 0 && lastDotIndex < path.length() - 1) {
            return path.substring(lastDotIndex + 1).toLowerCase();
        }

        return "";
    }

    private static String parseIpFromUrl(String url) {

        int start = url.indexOf("//");
        url = url.substring(start + 2);
        int end = url.indexOf("/");
        String tmp = url.substring(0, end);
        if (!tmp.contains(":")) {
            return tmp;
        }
        int index = tmp.indexOf(":");
        return tmp.substring(0, index);
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String uri = request.getRequestURI();
        List<String> suffixs = Arrays.asList("js", "css", "html", "htm", "ico", "png", "jpg", "gif", "map", "wasm");
        String suffix = getSuffixFromUrl(uri);
        if (suffixs.contains(suffix)) {
            return true;
        }

        request.getSession().setAttribute("accessStartTime", System.currentTimeMillis());
        // 首先校验token
        boolean isTokenValid = checkToken(request, response);
        if (!isTokenValid) {
            return false;
        }
        return checkSysAdminOperation(request, response);
    }

    private boolean checkSysAdminOperation(HttpServletRequest request, HttpServletResponse response) {
        String uri = request.getRequestURI();
        if ((uri.startsWith("/data/ed/prj") || uri.startsWith("/data/ed/repo/prj")) && !UserThreadLocal.getAdminType().equals(AdminTypeEnum.SYSTEM.getValue())) {
            log.warn("{}没有层级操作权限，当前用户类型是{}", UserThreadLocal.getUsername(), UserThreadLocal.getAdminType());
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            return false;
        }
        return true;
    }

    private boolean checkToken(HttpServletRequest request, HttpServletResponse response) {
        String token = request.getHeader("Authorization");
        String uri = request.getRequestURI();
        if (token == null) {
            log.error("Authorization header is null");
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        } else {
            token = token.substring(7);
        }
        boolean result = isTokenValid(token);
        if (!result) {
            log.error("Invalid token, uri is --->{}", uri);
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            return false;
        } else {
            Claims claims = TokenUtil.getLoginInfo(token, elePropertyConfig.getLoginEncPasswd());
            if (claims == null) {
                log.error("User info is missing, uri is --->{}", uri);
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return false;
            } else {
                UserLoginInfo userLoginInfo = new UserLoginInfo();
                userLoginInfo.setUserId(claims.get(UserConstants.LOGIN_USER_ID, String.class));
                userLoginInfo.setUsername(claims.get(UserConstants.LOGIN_USER_NAME, String.class));
                userLoginInfo.setWorkNumber(claims.get(UserConstants.LOGIN_WORK_NUMBER, String.class));
                userLoginInfo.setAdminType(claims.get(UserConstants.LOGIN_ADMIN_TYPE, String.class));
                userLoginInfo.setPrjTmpDir(elePropertyConfig.getEleTmpPath());
                UserThreadLocal.set(userLoginInfo);
            }
            return true;
        }
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        try {
            long accessStartTime = (long) request.getSession().getAttribute("accessStartTime");
            long accessEndTime = System.currentTimeMillis();
            UserOperation userOperation = ((HandlerMethod) handler).getMethod().getAnnotation(UserOperation.class);

            if (ObjectUtil.isEmpty(userOperation)) {
                return;
            }

            String reqArgs = UserThreadLocal.getReqArgs();

            UserLoginInfo user = Optional.of(UserThreadLocal.getUser()).orElse(new UserLoginInfo());
            List<AccessSuccessInfo> successInfoList = user.getSuccessInfoList();

            if (CollUtil.isEmpty(successInfoList)) {
                ElectromagneticResult res = user.getResult();
                UserAccessLog userAccessLog = new UserAccessLog()
                        .setId(EleIdCreator.createSnowflakeId())
                        .setUserId(user.getUserId())
                        .setAccessStartTime(DateUtil.date(accessStartTime))
                        .setAccessEndTime(DateUtil.date(accessEndTime))
                        .setAccessDuration(accessEndTime - accessStartTime)
                        .setAction(userOperation.value())
                        .setRequestUrl(request.getRequestURL().toString())
                        .setRequestIp(parseIpFromUrl(request.getRequestURL().toString()))
                        .setReqArgs(reqArgs)
                        .setRemoteAddr(getRealIp(request))
                        .setAccessSuccess(true)
                        .setOperationModule(userOperation.modelName().key)
                        .setOperationMsg(res.getErrorMessage())
                        .setDataId("")
                        .setParentId("");

                userAccessLog.setResponse(JSONUtil.toJsonStr(res));
                if (!res.getSuccess()) {
                    userAccessLog.setAccessSuccess(false);
                    userAccessLog.setOperationMsg(res.getErrorMessage());
                    userAccessLog.setExceptionDetail(UserThreadLocal.getUser().getExceptionDetail());
                }
                userAccessLogMapper.insert(userAccessLog);
            } else {
                for (AccessSuccessInfo accessSuccessInfo : successInfoList) {
                    UserAccessLog userAccessLog = new UserAccessLog()
                            .setId(EleIdCreator.createSnowflakeId())
                            .setUserId(user.getUserId())
                            .setAccessStartTime(DateUtil.date(accessStartTime))
                            .setAccessEndTime(DateUtil.date(accessEndTime))
                            .setAccessDuration(accessEndTime - accessStartTime)
                            .setAction(userOperation.value())
                            .setRequestUrl(request.getRequestURL().toString())
                            .setRequestIp(parseIpFromUrl(request.getRequestURL().toString()))
                            .setReqArgs(reqArgs)
                            .setRemoteAddr(getRealIp(request))
                            .setAccessSuccess(true)
                            .setOperationModule(userOperation.modelName().key)
                            .setOperationMsg(accessSuccessInfo.getSuccessMsg())
                            .setDataId(accessSuccessInfo.getDataId())
                            .setParentId(accessSuccessInfo.getParentId());

                    ElectromagneticResult<?> result = user.getResult();
                    if (result != null) {
                        userAccessLog.setResponse(JSONUtil.toJsonStr(result));
                        if (!result.getSuccess()) {
                            userAccessLog.setAccessSuccess(false);
                            userAccessLog.setOperationMsg(result.getErrorMessage());
                            userAccessLog.setExceptionDetail(UserThreadLocal.getUser().getExceptionDetail());
                        }
                    } else { // 返回为ResponseEntity，且状态为失败。
                        userAccessLog.setAccessSuccess(false);
                    }
                    userAccessLogMapper.insert(userAccessLog);
                }
            }
        } catch (Exception e) {
            log.warn("请求后置异常，原因 {}", e.getMessage(), e);
        } finally {
            UserThreadLocal.remove();
        }
    }

    public Boolean isTokenValid(String tokenStr) {
        Token token = tokenMapper.selectToken(tokenStr);
        Date now = new Date(SystemClock.now());
        return token != null && now.before(token.getExpireAt());
    }

}
